﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YetAnotherChat.ChatCore.Interfaces;
using YetAnotherChat.ChatCore.Models;
using YetAnotherChat.MemoryStorage.Interfaces;

namespace YetAnotherChat.MemoryStorage.Imp
{
    public class ChatMessageStore : IChatMessageStore
    {
        private ICache _cache;
        private const string ChatTopicCachePrefix = "ChatTopicCachePrefix_";
        private const int ChatTopicMaxMessageCountInCache = 200;
        private IMessageRepository _repo;

        public ChatMessageStore(ICache cache, IMessageRepository repository)
        {
            _cache = cache;
            _repo = repository;
        }

        private class ReversedDateTimeComparer : IComparer<DateTime>
        {
            public int Compare(DateTime x, DateTime y)
            {
                return DateTime.Compare(y, x);
            }
        }

        /// <summary>
        /// Add chat messages to cache
        /// </summary>
        /// <param name="messages"></param>
        public void AddMessages(ChatMessage[] messages)
        {
            var messageGroup = messages.GroupBy<ChatMessage, Guid>(c => c.ChatTopicGuidId);
            foreach (var group in messageGroup)
            { 
                var topicMessages= _cache.Get(ChatTopicCachePrefix/* +group.Key*/) as SortedList<DateTime, ChatMessage>;
                if (topicMessages == null)
                {
                    topicMessages = new SortedList<DateTime, ChatMessage>(new ReversedDateTimeComparer());
                    
                }
                foreach (var m in group)
                {
                    if (!topicMessages.ContainsKey(m.Date))
                        topicMessages.Add(m.Date, m);
                }
                 
                if (topicMessages.Count > ChatTopicMaxMessageCountInCache)
                {
                    while (topicMessages.Count - ChatTopicMaxMessageCountInCache > 0)
                    {
                        topicMessages.RemoveAt(topicMessages.Count - 1);
                    }
                }
                _cache.Insert(ChatTopicCachePrefix/* + group.Key*/, topicMessages);
            }
        }

        /// <summary>
        /// Get messages from cache and DB if needed
        /// </summary>
        /// <param name="topicGuid"></param>
        /// <param name="fromTime"></param>
        /// <param name="toTime"></param>
        /// <returns></returns>
        public ChatCore.Models.ChatMessage[] GetChatMessage(Guid topicGuid, Guid userId, DateTime fromTime, DateTime? toTime=null)
        {
            var topicMessages = _cache.Get(ChatTopicCachePrefix /*+ topicGuid.ToString()*/) as SortedList<DateTime, ChatMessage>;
            if (topicMessages == null)
            {
                topicMessages = new SortedList<DateTime, ChatMessage>(new ReversedDateTimeComparer());
                
            }
            if (topicMessages.Count == 0)
            {
                if (toTime == null)
                {
                    var fromDB = _repo.GetMany(m => /*m.ChatTopicGuidId == topicGuid &&*/ m.Date >= fromTime).OrderByDescending(m => m.Date);

                    foreach (var m in fromDB)
                    {
                        if (!topicMessages.ContainsKey(m.Date))
                            topicMessages.Add(m.Date, m);
                    }

                    _cache.Insert(ChatTopicCachePrefix /*+ topicGuid.ToString()*/, topicMessages);

                    return topicMessages.Values.Where(m => m.UserGuidId != userId && m.Date >= fromTime).ToArray();
                }
                else
                {
                    var fromDB = _repo.GetMany(m => /*m.ChatTopicGuidId == topicGuid &&*/ m.Date <= toTime && m.Date >= fromTime).OrderByDescending(m => m.Date);
                    foreach (var m in fromDB)
                    {
                        if (!topicMessages.ContainsKey(m.Date))
                            topicMessages.Add(m.Date, m);
                    }
                    _cache.Insert(ChatTopicCachePrefix /*+ topicGuid.ToString()*/, topicMessages);

                    return topicMessages.Values.Where(m => m.UserGuidId != userId && m.Date >= fromTime && m.Date <= toTime).ToArray();
                }
            }

            if (toTime == null)
            {
                if (topicMessages.Values[topicMessages.Count - 1].Date <= fromTime)
                {
                    List<ChatMessage> list = new List<ChatMessage>();
                    int i = 0;
                    while (topicMessages.Keys[i].Date >= fromTime)
                    {
                        if (topicMessages.Values[i].UserGuidId != userId)
                        {
                            list.Add(topicMessages.Values[i]);
                        }
                        i++;
                    }
                    return list.ToArray();
                }
                else
                {
                    var lastTime = topicMessages.Values[topicMessages.Count - 1].Date;
                    var fromDB = _repo.GetMany(m => /*m.ChatTopicGuidId == topicGuid &&*/ m.Date >= fromTime && m.Date <= lastTime).OrderByDescending(m => m.Date);

                    foreach(var m in fromDB)
                    {
                        if (!topicMessages.ContainsKey(m.Date))
                            topicMessages.Add(m.Date, m);
                    }
                    _cache.Insert(ChatTopicCachePrefix /*+ topicGuid.ToString()*/, topicMessages);

                    return topicMessages.Values.Where(m => m.UserGuidId != userId && m.Date >= fromTime).ToArray();
                }
            }
            else
            {
                if (topicMessages.Values[topicMessages.Count-1].Date <= fromTime)
                {
                    return topicMessages.Values.Where(m => m.UserGuidId != userId && m.Date <= toTime && m.Date >= fromTime).ToArray();
                }
                else
                {
                    var lastTime = topicMessages.Values[topicMessages.Count - 1].Date;
                    var fromDB = _repo.GetMany(m => /*m.ChatTopicGuidId == topicGuid &&*/ m.Date <= lastTime && m.Date >= fromTime).OrderByDescending(m => m.Date);
                    foreach (var m in fromDB)
                    {
                        if (!topicMessages.ContainsKey(m.Date))
                            topicMessages.Add(m.Date, m);
                    }
                    _cache.Insert(ChatTopicCachePrefix /*+ topicGuid.ToString()*/, topicMessages);

                    return topicMessages.Values.Where(m => m.UserGuidId != userId && m.Date >= fromTime && m.Date <= toTime).ToArray();
                }
            }

           
        }
    }
}
